package pl.edu.pb.wi.pwnography.tree;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import quickdt.Instance;

public class Entropy {
    // private static final Logger log =
    // Logger.getLogger(Entropy.class.getName());

    public static double calculateClassEntropy(Set<Instance> instances) {
	Map<Serializable, Integer> uniqueClasses = uniqueClasses(instances);

	double entropy = 0;
	for (Map.Entry<Serializable, Integer> entry : uniqueClasses.entrySet()) {
	    double probabiltiy = entry.getValue() / (double) instances.size();
	    entropy += -probabiltiy * Math.log(probabiltiy) / Math.log(2);
	}

	return entropy;
    }

    public static double calculateConditionalEntropy(Set<Instance> instances,
	    Serializable attribute) {
	Map<Serializable, Integer> uniqueClasses = uniqueClasses(instances);
	Map<Serializable, Integer> uniqueValues = new HashMap<Serializable, Integer>();
	for (Instance i : instances) {
	    Integer val = null;
	    if ((val = uniqueValues.get(i.attributes.get(attribute))) != null)
		uniqueValues.put(i.attributes.get(attribute), ++val);
	    else
		uniqueValues.put(i.attributes.get(attribute),
			Integer.valueOf(1));
	}

	double entropy = 0;
	for (Map.Entry<Serializable, Integer> entry : uniqueValues.entrySet()) {
	    double secondPart = 0;
	    for (Map.Entry<Serializable, Integer> uniqueClass : uniqueClasses
		    .entrySet()) {
		int count = 0;
		for (Instance i : instances) {
		    if (i.attributes.get(attribute).equals(entry.getKey())
			    && i.classification.equals(uniqueClass.getKey()))
			++count;
		}

		if (count > 0)
		    secondPart += (count / (double) entry.getValue())
			    * Math.log(count / (double) entry.getValue())
			    / Math.log(2);
	    }

	    double probabiltiy = entry.getValue() / (double) instances.size();
	    entropy += -probabiltiy * secondPart;
	}

	return entropy;
    }

    private static Map<Serializable, Integer> uniqueClasses(
	    Set<Instance> instances) {
	Map<Serializable, Integer> uniqueValues = new HashMap<Serializable, Integer>();
	for (Instance i : instances) {
	    Integer val = null;
	    if ((val = uniqueValues.get(i.classification)) != null)
		uniqueValues.put(i.classification, ++val);
	    else
		uniqueValues.put(i.classification, Integer.valueOf(1));
	}

	return uniqueValues;
    }
}
